﻿using System;
using System.Globalization;

namespace LinqToAmazonSL
{
    public abstract class OperationPerformer
    {
        //Function to update the structure "dr" according to the operation applied
        static public GuideRequest DoOperation(Operations operation, GuideRequest dr)
        {
            string paramName = "";
            switch (operation.Oper)
                {
                    case Oper.Count:
                        {
                            dr.LastOperation = Oper.Count;
                            break;
                        }
                    case Oper.First:
                        {
                            dr.LastOperation = Oper.First;
                            break;
                        }
                    case Oper.FirstOrDefault:
                        {
                            dr.LastOperation = Oper.FirstOrDefault;
                            break;
                        }
                    case Oper.Last:
                        {
                            dr.LastOperation = Oper.Last;
                            break;
                        }
                    case Oper.LastOrDefault:
                        {
                            dr.LastOperation = Oper.LastOrDefault;
                            break;
                        }
                    case Oper.ElementAt:
                        {
                            if (dr.Count > 0)
                            {
                                if (int.Parse(operation.Value, CultureInfo.CurrentCulture) >= dr.Count)
                                {
                                    paramName = "Position out of range in ElementAt";
                                    throw new ArgumentOutOfRangeException(paramName + ":operation.Value");
                                }
                            }
                            dr.PageBase = dr.PageBase + int.Parse(operation.Value, CultureInfo.CurrentCulture) / 10;
                            int dif = int.Parse(operation.Value, CultureInfo.CurrentCulture) % 10;
                            if((dr.ItemPageBase + dif)>9)
                            {
                                dr.ItemPageBase = (dr.ItemPageBase + dif) % 10;
                                dr.PageBase++;
                            }
                            else
                            {
                                dr.ItemPageBase = dr.ItemPageBase + dif;
                            }
                            dr.LastOperation = Oper.ElementAt;
                            break;
                        }
                    case Oper.ElementAtOrDefault:
                        {
                            if (dr.Count > 0)
                            {
                                if (int.Parse(operation.Value,CultureInfo.CurrentCulture) >= dr.Count)
                                {
                                    paramName = "Position out of range in ElementAtOrDefault";
                                    throw new ArgumentOutOfRangeException(paramName + ":operation.Value");
                                }
                            }
                            dr.PageBase = dr.PageBase + int.Parse(operation.Value,CultureInfo.CurrentCulture) / 10;
                            int dif = int.Parse(operation.Value,CultureInfo.CurrentCulture) % 10;
                            if((dr.ItemPageBase + dif)>9)
                            {
                                dr.ItemPageBase = (dr.ItemPageBase + dif) % 10;
                                dr.PageBase++;
                            }
                            else
                            {
                                dr.ItemPageBase = dr.ItemPageBase + dif;
                            }
                            dr.LastOperation = Oper.ElementAtOrDefault;
                            break;
                        }
                    case Oper.Any:
                        {
                            dr.LastOperation = Oper.Any;
                            break;
                        }
                    case Oper.Take:
                        {
                            dr.Count = int.Parse(operation.Value,CultureInfo.CurrentCulture);
                            dr.LastOperation = Oper.Take;
                            break;
                        }
                    case Oper.Skip:
                        {
                            if (dr.Count > 0)
                            {
                                dr.Count = dr.Count - int.Parse(operation.Value,CultureInfo.CurrentCulture);
                                if (dr.Count < 0)
                                {
                                    paramName = "Skip out of range:";
                                    throw new ArgumentOutOfRangeException(paramName + "dr.Count");
                                }
                                else if (dr.Count == 0)
                                {
                                    paramName="Empty collection:";
                                    throw new ArgumentNullException(paramName+"dr.Count");
                                }
                            }
                            dr.PageBase = dr.PageBase + int.Parse(operation.Value,CultureInfo.CurrentCulture) / 10;
                            int dif = int.Parse(operation.Value,CultureInfo.CurrentCulture) % 10;
                            if ((dr.ItemPageBase + dif) > 9)
                            {
                                dr.PageBase++;
                                dr.ItemPageBase = (dr.ItemPageBase + dif) % 10;
                            }
                            else
                            {
                                dr.ItemPageBase = dr.ItemPageBase + dif;
                            }
                            dr.LastOperation = Oper.Skip;
                            break;
                        }
                    default:
                        throw new InvalidOperationException("Unsupported operation");
                }
                return dr;
        }
    }
}
